﻿using System;
using System.ComponentModel;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Collections.ObjectModel;
using Microsoft.Phone.UserData;
using LiveEduWizard.Filter;
using System.Threading;


namespace LiveEduWizard
{
    public class MainViewModel : INotifyPropertyChanged
    {
        public MainViewModel()
        {
            //collections
            this.Timetables = new ObservableCollection<TimetableViewModel>();
            this.Agenda = new ObservableCollection<AgendaViewModel>();
            this.SubjectNames = new ObservableCollection<string>();

            //settings
            try
            {
                liveEduAccount = UserSettings.LiveEduAccount;
                allowsimultaneousScroll = UserSettings.SimultaneousTimetableGridScrolling;
            }
            catch (Exception e)  //Designer do not have isolated storage
            {
                //Default settings
                liveEduAccount = "live@edu";
                allowsimultaneousScroll = true;

                //report exception
                Debug.WriteLine(e.Message);
                Debug.WriteLine(e.StackTrace);
            }
        }

        public ObservableCollection<TimetableViewModel> Timetables { get; private set; }
        public ObservableCollection<AgendaViewModel> Agenda { get; private set; }
        public ObservableCollection<string> SubjectNames { get; private set; }

        public bool IsDataLoaded
        {
            get;
            private set;
        }

        /// <summary>
        /// Creates and adds a few ItemViewModel objects into the Items collection.
        /// </summary>
        public void LoadData()
        {
            MainPage.ShowProgressIndicator();

            Account account = null;
            var appointments = new Appointments();

            foreach(var acc in appointments.Accounts)
            {
                if (acc.Name == liveEduAccount)
                {
                    account = acc;
                    break;
                }
            }
            appointments.SearchCompleted += new EventHandler<Microsoft.Phone.UserData.AppointmentsSearchEventArgs>(appointments_SearchCompleted);
            appointments.SearchAsync(DateTime.Now.AddDays(-14), DateTime.Now.AddMonths(6), account, null);

            this.IsDataLoaded = true;
        }

        void appointments_SearchCompleted(object sender, Microsoft.Phone.UserData.AppointmentsSearchEventArgs e)
        {
            Thread loader = new Thread(new ParameterizedThreadStart(LoadWorker));
            loader.Start(e.Results);
        }

        void LoadWorker(object results)
        {
            TimetableViewModel timetable = null;
            AgendaViewModel agenda = null;
            AppointmentViewModel subject = null;
            AppointmentViewModel task = null;
            DateTime firstAppointmentStart = DateTime.MinValue;
            DateTime lastAppointmentEnd = DateTime.MinValue;
            LinkedList<TimetableViewModel> timetables = new LinkedList<TimetableViewModel>();
            LinkedList<AgendaViewModel> agendas = new LinkedList<AgendaViewModel>();
            List<string> subjectNames = new List<string>();

            foreach (var app in (IEnumerable<Appointment>)results)
            {
                var appointment = new AppointmentViewModel(app);

                if (firstAppointmentStart == DateTime.MinValue)
                    firstAppointmentStart = appointment.Start;

                lastAppointmentEnd = appointment.End;

                if (appointment.Type == AppointmentType.Subject)
                {
                    AddSubjectName(subjectNames, appointment.Title);

                    if (timetable == null || !timetable.TryAdd(appointment))
                    {
                        timetable = TimetableViewModel.Create(appointment);
                        timetables.AddLast(timetable);
                    }

                    subject = appointment;
                }
                else
                {
                    AddSubjectName(subjectNames, appointment.Subject);

                    if (agenda == null || !agenda.TryAdd(appointment))
                    {
                        agenda = AgendaViewModel.Create(appointment);
                        agendas.AddLast(agenda);
                    }

                    task = appointment;
                }

                if (subject != null && task != null
                    && task.Start >= subject.Start
                    && task.Start <= subject.End)
                    AppointmentViewModel.CreateRelation(subject, task);
            }

            Deployment.Current.Dispatcher.BeginInvoke(() =>
            {
                lock (this)
                {
                    ActiveAgenda = null;
                    ActiveTimetable = null;

                    Timetables.Clear();
                    Agenda.Clear();
                    SubjectNames.Clear();

                    foreach (var item in timetables)
                        Timetables.Add(item);
                    
                    foreach (var item in agendas)
                        Agenda.Add(item);

                    foreach (var item in subjectNames)
                        SubjectNames.Add(item);

                    FirstAppointmentStart = firstAppointmentStart;
                    LastAppointmentEnd = lastAppointmentEnd;

                    ResetActive();
                }

                MainPage.HideProgressIndicator();
            });
        }

        private void AddSubjectName(IList<string> subjectNames, string subjectName)
        {
            int down = 0;
            int up = subjectNames.Count;
            int i = (down + up) / 2;

            while (i >= down && i < up)
            {
                int comp = subjectName.CompareTo(subjectNames[i]);

                if (comp == 0)
                    return;

                if (comp < 0)
                {
                    up = i;
                }
                else
                {
                    down = i + 1;
                }

                i = (down + up) / 2;
            }

            subjectNames.Insert(i, subjectName);
        }

        private string liveEduAccount;
        public string LiveEduAccount
        {
            get
            {
                return liveEduAccount;
            }
            set
            {
                if (liveEduAccount != value)
                {
                    liveEduAccount = value;
                    LoadData();
                    NotifyPropertyChanged("LiveEduAccount");
                }
            }
        }

        private bool allowsimultaneousScroll;
        public bool AllowSimultaneousScroll
        {
            get
            {
                return allowsimultaneousScroll;
            }
            set
            {
                if (allowsimultaneousScroll != value)
                {
                    TimetableVerticalOffset = 0;
                    allowsimultaneousScroll = value;
                    NotifyPropertyChanged("AllowsimultaneousScroll");
                }
            }
        }

        private double timetableVerticalOffset = 0;
        public double TimetableVerticalOffset
        {
            get
            {
                return timetableVerticalOffset;
            }
            set
            {
                if (allowsimultaneousScroll && value != timetableVerticalOffset)
                {
                    timetableVerticalOffset = value;
                    NotifyPropertyChanged("TimetableVerticalOffset");
                }
            }
        }

        private TimetableViewModel activeTimetable;
        public TimetableViewModel ActiveTimetable
        {
            get
            {
                if (activeTimetable == null)
                    ResetActiveTimetable();

                return activeTimetable;
            }
            set
            {
                if (value != activeTimetable)
                {
                    activeTimetable = value;
                    NotifyPropertyChanged("ActiveTimetable");
                }
            }
        }

        private AgendaViewModel activeAgenda;
        public AgendaViewModel ActiveAgenda
        {
            get
            {
                if (activeAgenda == null)
                    ResetActiveAgenda();

                return activeAgenda;
            }
            set
            {
                if (value != activeAgenda)
                {
                    activeAgenda = value;
                    NotifyPropertyChanged("ActiveAgenda");
                }
            }
        }

        private AppointmentViewModel activeAppointment = null;
        public AppointmentViewModel ActiveAppointment
        {
            get
            {
                return activeAppointment;
            }
            set
            {
                if (value != activeAppointment)
                {
                    activeAppointment = value;
                    NotifyPropertyChanged("ActiveAppointment");
                }
            }
        }

        private IFilterRule filterRule = null;
        public IFilterRule FilterRule
        {
            get
            {
                return filterRule;
            }
            set
            {
                if (value != filterRule)
                {
                    filterRule = value;
                    NotifyPropertyChanged("FilterRule");
                }
            }
        }

        private DateTime firstAppointmentStart;
        public DateTime FirstAppointmentStart
        {
            get
            {
                return firstAppointmentStart;
            }
            set
            {
                if (value != firstAppointmentStart)
                {
                    firstAppointmentStart = value;
                    NotifyPropertyChanged("FirstAppointmentStart");
                }
            }
        }

        private DateTime lastAppointmentEnd;
        public DateTime LastAppointmentEnd
        {
            get
            {
                return lastAppointmentEnd;
            }
            set
            {
                if (value != lastAppointmentEnd)
                {
                    lastAppointmentEnd = value;
                    NotifyPropertyChanged("LastAppointmentEnd");
                }
            }
        }

        public void ResetActive()
        {
            ResetActiveAgenda();
            ResetActiveTimetable();
            ActiveAppointment = null;
        }
        
        public void ResetActiveAgenda()
        {
            DateTime now = DateTime.Now.Date;
            
            lock (this)
            {
                AgendaViewModel newActiveAgenda = null;

                if (Agenda.Count > 0)
                {
                    newActiveAgenda = Agenda[Agenda.Count - 1];
                    
                    for (int i = Agenda.Count - 2; i >= 0 && Agenda[i].Date >= now; i--)
                    {
                        newActiveAgenda = Agenda[i];
                    }
                }

                ActiveAgenda = newActiveAgenda;
            }
        }

        public void ResetActiveTimetable()
        {   
            lock (this)
            {
                TimetableViewModel newActiveTimetable = null;

                if (Timetables.Count > 0)
                {
                    AppointmentViewModel nowApp = new AppointmentViewModel();
                    nowApp.Start = DateTime.Now;

                    if (nowApp.Start.DayOfWeek == DayOfWeek.Sunday || nowApp.Start.DayOfWeek == DayOfWeek.Saturday)
                        nowApp.Start = nowApp.Start.AddDays(2);

                    newActiveTimetable = Timetables[Timetables.Count - 1];

                    for (int i = Timetables.Count - 2; i >= 0 && (Timetables[i].Year > nowApp.WeekYear
                        || Timetables[i].Year == nowApp.WeekYear && Timetables[i].Week >= nowApp.Week); i--)
                    {
                        newActiveTimetable = Timetables[i];
                    }
                }

                ActiveTimetable = newActiveTimetable;
                timetableVerticalOffset = 0;
                NotifyPropertyChanged("TimetableVerticalOffset");
            }
        }

        public void SelectAgendaByWeek(int week, int year)
        {
            lock (this)
            {
                AgendaViewModel newActiveAgenda = null;

                if (Agenda.Count > 0)
                {
                    newActiveAgenda = Agenda[Agenda.Count - 1];

                    for (int i = Agenda.Count - 2; i >= 0 &&
                        (Agenda[i].WeekYear > year || Agenda[i].WeekYear == year && Agenda[i].Week >= week); i--)
                    {
                        newActiveAgenda = Agenda[i];
                    }
                }

                ActiveAgenda = newActiveAgenda;
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
        private void NotifyPropertyChanged(String propertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (null != handler)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }        
    }
}